//
// Created by 谢乙田 on 2025/7/8.
//

#ifndef LLVMEVAL_H
#define LLVMEVAL_H
#include <string>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
class LLVMEVAL {
public:
    LLVMEVAL() {
        ctx=std::make_unique<llvm::LLVMContext>();
        module=std::make_unique<llvm::Module>("LLVMEVAL ",*ctx);
        builder=std::make_unique<llvm::IRBuilder<>>(*ctx);
    }
    void exec(const std::string& program)  {
        module->print(llvm::outs(),nullptr);
        compile();
        saveModuleToFile("/Users/jimo/CLionProjects/llvmdemo/out.ll");
    }
private:
    llvm::BasicBlock* createBB(std::string name ,llvm::Function *func=nullptr) const {
        return  llvm::BasicBlock::Create(*ctx,name,func);
    }

    void createFunctionBlock(llvm::Function * func) const {
        auto entry=createBB("entry",func);
        builder->SetInsertPoint(entry);

    }
    llvm::Function* createFunction(const std::string&functionName ,llvm::FunctionType* functionType) const {
        auto func=module->getFunction(functionName);

        if (func==nullptr) {
            func=createFunctionProto(functionName,functionType);
        }
        createFunctionBlock(func);
        return func;
    }

    llvm::Function* createFunctionProto(const std::string&functionName ,llvm::FunctionType* functionType) const {
        llvm::Function* func=llvm::Function::Create(functionType,
            llvm::GlobalValue::ExternalLinkage,functionName,*module);
        verifyFunction(*func);
        return func;
    }
    llvm::Value* gen() const {
        return builder->getInt32(42);
    }
    void saveModuleToFile(const std::string& fileName) const {
        std::error_code errorCode;
        llvm::raw_fd_ostream outLL(fileName, errorCode);
        module->print(outLL,nullptr);
    }
    void compile() {
        fn=createFunction("main", llvm::FunctionType::get(builder->getInt32Ty(),false));
         const auto result=gen();
         const auto i32Result=builder->CreateIntCast(result,builder->getInt32Ty(),true);
        builder->CreateRet(i32Result);
    }
    std::unique_ptr<llvm::LLVMContext> ctx;
    std::unique_ptr<llvm::Module> module;
    std::unique_ptr<llvm::IRBuilder<>> builder;
    llvm::Function * fn;
};
#endif